/**********************************************************************
 * SailmiPIM - modular cross-platform feature Personal Information Manager Tools.
 * Copyright(C) 2006-2014  Georg Rudoy
 *
 * Distributed under the Boost Software License, Version 1.0.
 *(See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
 **********************************************************************/

#pragma once

#include <util/sll/either.h>
#include "task.h"

namespace SailmiPIM::Util
{
	template<typename L, std::default_initializable R>
	struct IgnoreLeft
	{
		Either<L, R> Result_;

		bool await_ready() const noexcept
		{
			return true;
		}

		void await_suspend(auto) const noexcept
		{
		}

		R await_resume() const noexcept
		{
			return RightOr(Result_, R {});
		}
	};

	template<typename L, typename R>
	IgnoreLeft(Either<L, R>) -> IgnoreLeft<L, R>;

	namespace detail
	{
		template<typename ErrorHandler>
		struct EitherAwaiterErrorHandler
		{
			ErrorHandler Handler_;

			template<typename L>
			void HandleError(L&& left)
			{
				Handler_(std::forward<L>(left));
			}
		};

		template<>
		struct EitherAwaiterErrorHandler<void>
		{
			void HandleError(auto&&)
			{
			}
		};

		template<typename L, typename R, typename ErrorHandler = void>
		struct EitherAwaiter
		{
			Either<L, R> Either_;
			EitherAwaiterErrorHandler<ErrorHandler> Handler_ {};

			bool await_ready() const noexcept
			{
				return Either_.IsRight();
			}

			template<typename Promise>
			void await_suspend(std::coroutine_handle<Promise> handle)
			{
				Handler_.HandleError(Either_.GetLeft());

				auto& promise = handle.promise();
				if constexpr(Promise::IsVoid)
					promise.return_void();
				else
					promise.return_value(Promise::ReturnType_t::Left(Either_.GetLeft()));

				throw EitherFailureAbort {};
			}

			R await_resume() const noexcept
			{
				return Either_.GetRight();
			}
		};
	}

	template<typename L, typename R, typename F>
		requires std::invocable<F, const L&>
	Util::detail::EitherAwaiter<L, R, F> WithHandler(const Util::Either<L, R>& either, F&& errorHandler)
	{
		return { either, { std::forward<F>(errorHandler) } };
	}
}

namespace SailmiPIM
{
	template<typename L, typename R>
	Util::detail::EitherAwaiter<L, R> operator co_await(const Util::Either<L, R>& either)
	{
		return { either };
	}
}
